package com.rcclabs.commonutils.gdata.docs.spreadsheet.services.impl;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.spreadsheet.CellEntry;
import com.google.gdata.data.spreadsheet.CellFeed;
import com.google.gdata.data.spreadsheet.ListEntry;
import com.google.gdata.data.spreadsheet.ListFeed;
import com.google.gdata.data.spreadsheet.SpreadsheetEntry;
import com.google.gdata.data.spreadsheet.SpreadsheetFeed;
import com.google.gdata.data.spreadsheet.WorksheetEntry;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.rcclabs.commonutils.gdata.common.DocumentType;
import com.rcclabs.commonutils.gdata.docs.service.impl.DocumentListServiceImpl;
import com.rcclabs.commonutils.gdata.docs.spreadsheet.common.SpreadsheetConstants;
import com.rcclabs.commonutils.gdata.docs.spreadsheet.services.GoogleSpreadsheetService;
import com.rcclabs.commonutils.gdata.exceptions.DocumentListException;

public class GoogleSpreadsheetServiceImpl extends DocumentListServiceImpl
		implements GoogleSpreadsheetService {

	private SpreadsheetService service;

	public GoogleSpreadsheetServiceImpl(String applicationName,
			String userName, String password) throws DocumentListException,
			AuthenticationException {
		super(applicationName, userName, password);

		service = new SpreadsheetService(applicationName);
		service.setUserCredentials(userName, password);
	}

	public GoogleSpreadsheetServiceImpl(String applicationName, String token)
			throws DocumentListException, AuthenticationException {
		super(applicationName, token);

		service = new SpreadsheetService(applicationName);
		service.setAuthSubToken(token);
	}

	@Override
	public DocumentListEntry create(String title) throws DocumentListException,
			MalformedURLException, IOException, ServiceException {
		return super.create(title, DocumentType.SPREADSHEET);
	}

	@Override
	public SpreadsheetFeed getSpreadsheetFeed() throws MalformedURLException,
			IOException, ServiceException {
		return service.getFeed(new URL(
				SpreadsheetConstants.SPREADSHEET_FEED_URL),
				SpreadsheetFeed.class);
	}

	@Override
	public List<SpreadsheetEntry> getSpreadsheetList()
			throws MalformedURLException, IOException, ServiceException {
		SpreadsheetFeed feed = service.getFeed(new URL(
				SpreadsheetConstants.SPREADSHEET_FEED_URL),
				SpreadsheetFeed.class);
		return (feed != null) ? feed.getEntries() : null;
	}

	@Override
	public SpreadsheetEntry getSpreadsheet(String key)
			throws MalformedURLException, IOException, ServiceException {
		return service.getEntry(new URL(
				SpreadsheetConstants.SPREADSHEET_ENTRY_BASE_URL + "/" + key),
				SpreadsheetEntry.class);
	}

	@Override
	public WorksheetEntry getDefaultWorksheet(String key)
			throws MalformedURLException, IOException, ServiceException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);
		return (spreadsheet != null) ? spreadsheet.getDefaultWorksheet() : null;
	}

	public WorksheetEntry getWorksheet(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException {
		List<WorksheetEntry> worksheets = getWorksheetList(key);
		return worksheets.get(worksheetIndex);
	}

	@Override
	public List<WorksheetEntry> getWorksheetList(String key)
			throws MalformedURLException, IOException, ServiceException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);
		return (spreadsheet != null) ? spreadsheet.getWorksheets() : null;
	}

	@Override
	public void insertWorksheet(String key, String title)
			throws MalformedURLException, IOException, ServiceException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);

		if (spreadsheet == null)
			return;

		WorksheetEntry worksheet = new WorksheetEntry();
		worksheet.setTitle(new PlainTextConstruct(title));

		URL feedUrl = spreadsheet.getWorksheetFeedUrl();
		service.insert(feedUrl, worksheet);
	}

	@Override
	public void insertWorksheet(String key, String title, int rows, int columns)
			throws MalformedURLException, IOException, ServiceException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);

		if (spreadsheet == null)
			return;

		WorksheetEntry worksheet = new WorksheetEntry();
		worksheet.setTitle(new PlainTextConstruct(title));
		worksheet.setColCount(rows);
		worksheet.setRowCount(columns);

		URL feedUrl = spreadsheet.getWorksheetFeedUrl();
		service.insert(feedUrl, worksheet);
	}

	@Override
	public URL getCellFeedUrl(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		WorksheetEntry worksheet = getWorksheet(key, worksheetIndex);
		return worksheet.getCellFeedUrl();
	}

	@Override
	public URL getCellFeedUrl(String key, int worksheetIndex, Integer minRow,
			Integer maxRow, Integer minColumn, Integer maxColumn)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		WorksheetEntry worksheet = getWorksheet(key, worksheetIndex);
		String feedUrlString = worksheet.getListFeedUrl().toString();

		if (minRow != null)
			feedUrlString = feedUrlString
					+ ((feedUrlString.contains("?") ? "&" : "?")) + "min-row="
					+ minRow.intValue();

		if (maxRow != null)
			feedUrlString = feedUrlString
					+ ((feedUrlString.contains("?") ? "&" : "?")) + "max-row="
					+ maxRow.intValue();

		if (minColumn != null)
			feedUrlString = feedUrlString
					+ ((feedUrlString.contains("?") ? "&" : "?")) + "min-col="
					+ minColumn.intValue();

		if (maxColumn != null)
			feedUrlString = feedUrlString
					+ ((feedUrlString.contains("?") ? "&" : "?")) + "max-col="
					+ maxColumn.intValue();

		return new URI(feedUrlString).toURL();
	}

	@Override
	public CellFeed getCellFeed(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		return service.getFeed(getCellFeedUrl(key, worksheetIndex),
				CellFeed.class);
	}

	@Override
	public CellFeed getCellFeed(String key, int worksheetIndex, Integer minRow,
			Integer maxRow, Integer minColumn, Integer maxColumn)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		return service.getFeed(
				getCellFeedUrl(key, worksheetIndex, minRow, maxRow, minColumn,
						maxColumn), CellFeed.class);
	}

	@Override
	public Set<String> getHeaders(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		CellFeed feed = service.getFeed(getCellFeedUrl(key, worksheetIndex),
				CellFeed.class);
		List<CellEntry> cells = feed.getEntries();
		Set<String> headers = new HashSet<String>();
		for (CellEntry cell : cells) {
			if (cell.getTitle().getPlainText().endsWith("1")) {
				headers.add(cell.getCell().getValue());
			}
		}
		return headers;
	}

	@Override
	public List<CellEntry> getCells(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		return service.getFeed(getCellFeedUrl(key, worksheetIndex),
				CellFeed.class).getEntries();
	}

	@Override
	public void insertCell(String key, int worksheetIndex, CellEntry cell)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		service.insert(this.getCellFeedUrl(key, worksheetIndex), cell);
	}

	@Override
	public URL getListFeedUrl(String key, int worksheetIndex, String query,
			boolean reverse, String orderBy) throws MalformedURLException,
			IOException, ServiceException, URISyntaxException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);
		if (spreadsheet == null)
			return null;

		List<WorksheetEntry> worksheets = spreadsheet.getWorksheets();
		if (worksheets == null || worksheets.size() == 0)
			return null;

		WorksheetEntry worksheet = worksheets.get(worksheetIndex);
		if (worksheet == null)
			return null;

		String feedUrlString = worksheet.getListFeedUrl().toString();

		if (query != null && !query.isEmpty()) {
			String urlEncodedQuery = URLEncoder.encode(query, "UTF-8");
			feedUrlString = feedUrlString + "?sq=" + urlEncodedQuery;
		}

		if (reverse) {
			if (feedUrlString.contains("?"))
				feedUrlString = feedUrlString + "&reverse=true";
			else
				feedUrlString = feedUrlString + "?reverse=true";
		}

		if (orderBy != null && !orderBy.isEmpty()) {
			if (feedUrlString.contains("?"))
				feedUrlString = feedUrlString + "&orderby=" + orderBy;
			else
				feedUrlString = feedUrlString + "?orderby=" + orderBy;
		}

		return new URI(feedUrlString).toURL();
	}

	@Override
	public ListFeed getListFeed(String key, int worksheetIndex, String query,
			boolean reverse, String orderBy) throws MalformedURLException,
			IOException, ServiceException, URISyntaxException {
		SpreadsheetEntry spreadsheet = getSpreadsheet(key);
		if (spreadsheet == null)
			return null;

		List<WorksheetEntry> worksheets = spreadsheet.getWorksheets();
		if (worksheets == null || worksheets.size() == 0)
			return null;

		WorksheetEntry worksheet = worksheets.get(worksheetIndex);
		if (worksheet == null)
			return null;

		String feedUrlString = worksheet.getListFeedUrl().toString();

		if (query != null && !query.isEmpty()) {
			String urlEncodedQuery = URLEncoder.encode(query, "UTF-8");
			feedUrlString = feedUrlString + "?sq=" + urlEncodedQuery;
		}

		if (reverse) {
			if (feedUrlString.contains("?"))
				feedUrlString = feedUrlString + "&reverse=true";
			else
				feedUrlString = feedUrlString + "?reverse=true";
		}

		if (orderBy != null && !orderBy.isEmpty()) {
			if (feedUrlString.contains("?"))
				feedUrlString = feedUrlString + "&orderby=" + orderBy;
			else
				feedUrlString = feedUrlString + "?orderby=" + orderBy;
		}

		URL feedUrl = new URI(feedUrlString).toURL();
		return service.getFeed(feedUrl, ListFeed.class);
	}

	@Override
	public List<ListEntry> getRows(String key, int worksheetIndex)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		ListFeed listFeed = getListFeed(key, worksheetIndex, null, false, null);
		return (listFeed != null) ? listFeed.getEntries() : null;
	}

	@Override
	public List<ListEntry> getRows(String key, int worksheetIndex,
			String query, boolean reverse, String orderBy)
			throws MalformedURLException, IOException, ServiceException,
			URISyntaxException {
		ListFeed listFeed = getListFeed(key, worksheetIndex, query, reverse,
				orderBy);

		return (listFeed != null) ? listFeed.getEntries() : null;
	}

	@Override
	public void insertRow(String key, int worksheetIndex,
			Map<String, String> elements) throws MalformedURLException,
			IOException, ServiceException, URISyntaxException {

		List<WorksheetEntry> worksheets = getWorksheetList(key);
		WorksheetEntry worksheet = worksheets.get(worksheetIndex);
		URL listFeedUrl = worksheet.getListFeedUrl();

		ListEntry listEntry = new ListEntry();
		Set<String> headers = elements.keySet();

		for (String header : headers) {
			listEntry.getCustomElements().setValueLocal(header,
					elements.get(header));
		}

		service.insert(listFeedUrl, listEntry);
	}

	@Override
	public void updateRow(String key, int worksheetIndex, int rowIndex,
			Map<String, String> elements) throws MalformedURLException, IOException, ServiceException {
		List<WorksheetEntry> worksheets = getWorksheetList(key);
		WorksheetEntry worksheet = worksheets.get(worksheetIndex);
		URL feedUrl = worksheet.getListFeedUrl();
		ListFeed feed = service.getFeed(feedUrl, ListFeed.class);	
		List<ListEntry> listEntries = feed.getEntries();
		ListEntry listEntry = listEntries.get(rowIndex);
		
		Set<String> headers = elements.keySet();
		for (String header : headers) {
			listEntry.getCustomElements().setValueLocal(header,
					elements.get(header));
		}
		service.update(feedUrl, listEntry);
	}

}
